////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2017-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  camxransacnode.cpp
/// @brief RANSAC Node class implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "camxnode.h"
#include "camxransacnode.h"
#include "camxcdmdefs.h"
#include "camxcslresourcedefs.h"
#include "camximagebuffer.h"
#include "camximageformatutils.h"
#include "camxhwcontext.h"
#include "camxtitan17xcontext.h"
#include "camxtitan17xdefs.h"
#include "camxtrace.h"
#include "camxlrmeproperty.h"
#include "TransformEstimation.h"
#include "camxtitan17xcontext.h"
#include "camxvendortags.h"

CAMX_NAMESPACE_BEGIN

const static UINT g_RANSACMaxInputPorts  = 1;
const static UINT g_RANSACMaxOutputPorts = 0;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::RANSACNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RANSACNode::RANSACNode()
{
    m_pNodeName = "RANSAC";
    m_numInputPorts = g_RANSACMaxInputPorts;
    m_numOutputPorts = g_RANSACMaxOutputPorts;
    m_derivedNodeHandlesMetaDone = TRUE;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::~RANSACNode
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RANSACNode::~RANSACNode()
{
    Cleanup();
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::Create
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RANSACNode* RANSACNode::Create(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateInputData);
    CAMX_UNREFERENCED_PARAM(pCreateOutputData);

    return CAMX_NEW RANSACNode;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::Cleanup
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID RANSACNode::Cleanup()
{
    return;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::ProcessingNodeInitialize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult RANSACNode::ProcessingNodeInitialize(
    const NodeCreateInputData* pCreateInputData,
    NodeCreateOutputData*      pCreateOutputData)
{
    CAMX_UNREFERENCED_PARAM(pCreateInputData);

    CamxResult        result                   = CamxResultSuccess;

    CAMX_ASSERT(RANSAC == Type());
    CAMX_ASSERT(NULL != pCreateOutputData);

    pCreateOutputData->maxOutputPorts = m_numInputPorts;
    pCreateOutputData->maxInputPorts  = m_numOutputPorts;

    if (CamxResultSuccess != result)
    {
        Cleanup();
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// RANSACNode::ProcessingNodeFinalizeInputRequirement
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult RANSACNode::ProcessingNodeFinalizeInputRequirement(
    BufferNegotiationData* pBufferNegotiationData)
{
    UINT32 numInputPorts = 0;
    UINT32 inputPortId[RANSACMaxInput];

    UINT maxBlocks = Utils::Ceiling(540.0f / 12.0f) * Utils::Ceiling(360.0f / 8.0f);
    UINT minBlocks = Utils::Ceiling(36.0f / 12.0f) * Utils::Ceiling(24.0f / 8.0f);
    UINT optBlocks = Utils::Ceiling(240.0f / 12.0f) * Utils::Ceiling(136.0f / 8.0f);
    UINT32 maxOutputWidth = maxBlocks * 6;
    UINT32 minOutputWidth = minBlocks * 6;
    UINT32 optOutputWidth = optBlocks * 6;
    // Get Input Port List
    GetAllInputPortIds(&numInputPorts, &inputPortId[0]);

    for (UINT input = 0; input < numInputPorts; input++)
    {
        pBufferNegotiationData->inputBufferOptions[input].nodeId = Type();
        pBufferNegotiationData->inputBufferOptions[input].instanceId = InstanceID();
        pBufferNegotiationData->inputBufferOptions[input].portId = inputPortId[input];

        BufferRequirement* pInputBufferRequirement = &pBufferNegotiationData->inputBufferOptions[input].bufferRequirement;
        pInputBufferRequirement->maxWidth = maxOutputWidth;
        pInputBufferRequirement->maxHeight = 1;
        pInputBufferRequirement->minWidth = minOutputWidth;
        pInputBufferRequirement->minHeight = 1;
        pInputBufferRequirement->optimalWidth = optOutputWidth;
        pInputBufferRequirement->optimalHeight = 1;
    }

    return CamxResultSuccess;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::ExecuteProcessRequest
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult RANSACNode::ExecuteProcessRequest(
    ExecuteProcessRequestData* pExecuteProcessRequestData)
{
    CAMX_ASSERT(NULL != pExecuteProcessRequestData);

    CamxResult result                        = CamxResultSuccess;
    NodeProcessRequestData* pNodeRequestData = pExecuteProcessRequestData->pNodeProcessRequestData;
    PerRequestActivePorts*  pEnabledPorts    = pExecuteProcessRequestData->pEnabledPortsInfo;
    UINT                    dependencyIndex  = 0;

    CAMX_ASSERT(NULL != pNodeRequestData);
    CAMX_ASSERT(NULL != pEnabledPorts);
    CAMX_ASSERT(NULL != pNodeRequestData->pCaptureRequest);

    if (0 == pNodeRequestData->processSequenceId)
    {
        for (UINT portIndex = 0; portIndex < pEnabledPorts->numInputPorts; portIndex++)
        {
            PerRequestInputPortInfo* pPerRequestInputPort = &pEnabledPorts->pInputPorts[portIndex];
            if (NULL != pPerRequestInputPort)
            {
                UINT fenceCount = pNodeRequestData->dependencyInfo[dependencyIndex].bufferDependency.fenceCount;
                pNodeRequestData->dependencyInfo[dependencyIndex].bufferDependency.phFences[fenceCount] =
                    pPerRequestInputPort->phFence;
                pNodeRequestData->dependencyInfo[dependencyIndex].bufferDependency.pIsFenceSignaled[fenceCount] =
                    pPerRequestInputPort->pIsFenceSignaled;
                CAMX_LOG_VERBOSE(CamxLogGroupLRME, "ransac Fence wait %d",
                    pPerRequestInputPort->phFence[0]);
                pNodeRequestData->dependencyInfo[dependencyIndex].bufferDependency.fenceCount++;
            }
        }

        if (0 < pNodeRequestData->dependencyInfo[dependencyIndex].bufferDependency.fenceCount)
        {
            pNodeRequestData->dependencyInfo[dependencyIndex].dependencyFlags.hasBufferDependency = TRUE;
            pNodeRequestData->dependencyInfo[dependencyIndex].processSequenceId = 1;
            pNodeRequestData->numDependencyLists++;
        }
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupLRME, "Ransac request %lld dependency signalled\n",
            pNodeRequestData->pCaptureRequest->requestId);

        CPerspectiveTransform transform;
        INT32 confidence = 0;
        // Get the lrme property
        VOID*        pPData[1] = { 0 };
        UINT64       pDataOffset[1] = { 0 };

        LRMEPropertyFrameSettings* pFrameSettings;

        static const UINT PropertiesLRMEFrameSetting[] = { PropertyIDLRMEFrameSettings };
        result = GetDataList(PropertiesLRMEFrameSetting, pPData, pDataOffset, 1);

        if (CamxResultSuccess == result)
        {
            pFrameSettings = static_cast<LRMEPropertyFrameSettings*>(pPData[0]);
            if (NULL == pFrameSettings)
            {
                CAMX_LOG_ERROR(CamxLogGroupLRME, "Unable to get PropertyIDLRMEFrameSettings");
                result = CamxResultEFailed;
            }
        }
        if (CamxResultSuccess == result)
        {
            CAMX_LOG_INFO(CamxLogGroupLRME, "LRME frame setting recv at ransac stepx: %d stepy %d refValid %d resFormat %d"
                " taroffsetx %d taroffsety %d refoffsetx %d refoffsety %d subpelsearch %d tarw %d tarh %d upscalefactor %d",
                pFrameSettings->LRMEStepX, pFrameSettings->LRMEStepY, pFrameSettings->LRMERefValid,
                pFrameSettings->LRMEresultFormat, pFrameSettings->LRMETarOffsetX, pFrameSettings->LRMETarOffsetY,
                pFrameSettings->LRMERefOffsetX, pFrameSettings->LRMERefOffsetY, pFrameSettings->LRMEsubpelSearchEnable,
                pFrameSettings->LRMETarW, pFrameSettings->LRMETarH, pFrameSettings->LRMEUpscaleFactor);

            if (1 != pNodeRequestData->pCaptureRequest->requestId)
            {
                ChannelType* pAddr = reinterpret_cast<ChannelType*>
                    (pEnabledPorts->pInputPorts[0].pImageBuffer->GetPlaneVirtualAddr(0, 0));

                CamxTime tv1;
                CamxTime tv2;
                CamX::OsUtils::GetTime(&tv1);
                UINT64 start = (tv1.seconds * 1000) + (tv1.nanoSeconds / 1000000);

                if (NULL == pAddr ||
                    NULL == pEnabledPorts->pInputPorts[0].pImageBuffer->GetFormat() ||
                    FALSE == ProcessMeResult(pAddr,
                        pEnabledPorts->pInputPorts[0].pImageBuffer->GetFormat()->width,
                        pEnabledPorts->pInputPorts[0].pImageBuffer->GetFormat()->height,
                        pFrameSettings->LRMETarW, pFrameSettings->LRMETarH,
                        pFrameSettings->LRMETarOffsetX, pFrameSettings->LRMETarOffsetY,
                        pFrameSettings->LRMERefOffsetX, pFrameSettings->LRMERefOffsetY,
                        pFrameSettings->LRMEStepX, pFrameSettings->LRMEStepY,
                        pFrameSettings->LRMEresultFormat,
                        pFrameSettings->LRMEsubpelSearchEnable, pFrameSettings->LRMEUpscaleFactor,
                        1, transform, confidence))
                {
                    result = CamxResultEFailed;
                }
                CamX::OsUtils::GetTime(&tv2);
                UINT64 end = (tv2.seconds * 1000) + (tv2.nanoSeconds / 1000000);
                CAMX_LOG_VERBOSE(CamxLogGroupLRME, "nclib ransac result %d confidence %d time %llu", result, confidence,
                    end - start);
            }
        }
        if (CamxResultSuccess == result)
        {
            CAMX_LOG_VERBOSE(CamxLogGroupLRME, "Posting for request %llu", pNodeRequestData->pCaptureRequest->requestId);
            result = PostICATransform(&transform, confidence, pFrameSettings);
        }
        ProcessMetadataDone(pNodeRequestData->pCaptureRequest->requestId);
        ProcessRequestIdDone(pNodeRequestData->pCaptureRequest->requestId);
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::IsNodeDisabledWithOverride
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOL RANSACNode::IsNodeDisabledWithOverride()
{
    if (TRUE == GetHwContext()->GetStaticSettings()->enableMCTF)
    {
        return FALSE;
    }
    else
    {
        CAMX_LOG_ERROR(CamxLogGroupLRME, "Ransac Disabled from settings");
        return TRUE;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::PostICATransform
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult RANSACNode::PostICATransform(
    VOID* pTrans,
    INT32 confidence,
    LRMEPropertyFrameSettings* pFrameSettings)
{
    IPEICAPerspectiveTransform  perspectiveTransform;
    CamxResult result = CamxResultSuccess;
    CPerspectiveTransform* pTransform = reinterpret_cast<CPerspectiveTransform *>(pTrans);

    perspectiveTransform.perspectiveGeometryNumRows  = 1;
    perspectiveTransform.perspetiveGeometryNumolumns = 1;
    perspectiveTransform.perspectiveConfidence       = confidence;
    memcpy(perspectiveTransform.perspectiveTransformArray, &pTransform->m, 3 * 3 * sizeof(FLOAT));
    perspectiveTransform.transformDefinedOnHeight    = pFrameSettings->LRMETarH * pFrameSettings->LRMEUpscaleFactor;
    perspectiveTransform.transformDefinedOnWidth     = pFrameSettings->LRMETarW * pFrameSettings->LRMEUpscaleFactor;
    perspectiveTransform.ReusePerspectiveTransform   = 0;
    perspectiveTransform.perspectiveTransformEnable  = 1;

    // Get the lrme property
    const VOID*  pPData[1] = { 0 };
    UINT         pDataSize[1] = { 0 };

    pPData[0] = &perspectiveTransform;
    pDataSize[0] = sizeof(IPEICAPerspectiveTransform);
    UINT IPEICATAGLocation = 0;
    CAMX_ASSERT(CamxResultSuccess == VendorTagManager::QueryVendorTagLocation("org.quic.camera2.ipeicaconfigs",
                                                                              "ICAReferenceParams",
                                                                              &IPEICATAGLocation));

    result = WriteDataList(&IPEICATAGLocation, pPData, pDataSize, 1);
    if (CamxResultSuccess != result)
    {
        CAMX_LOG_ERROR(CamxLogGroupLRME, "Failed to publish ICA transform");
    }
    else
    {
        CAMX_LOG_INFO(CamxLogGroupLRME, "Ransac posted ICA : Confidence %d res %dX%d", confidence,
            pFrameSettings->LRMETarH, pFrameSettings->LRMETarW);
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// RANSACNode::Destroy
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID RANSACNode::Destroy()
{
    CAMX_DELETE this;
}

CAMX_NAMESPACE_END
